<template>
	<view class="backf p20 main-color">
		<view class="mt10 mb30 process-title f36">流程</view>
		<view v-for="(task, index) in processTasks" :key="index">
			<view class="df jsb f30">
				<view class="df">
					<view class="flex-column mt10 mr20">
						<view class="item-round mb10"></view>
						<view
							class="item-line"
							v-if="Number(index + 1) < processTasks.length"
						></view>
					</view>
					<view class="title-desc">
						<view>{{ task.title }}</view>
						<view v-if="task.desc" class="f28">{{
							task.desc
						}}</view>
					</view>
				</view>
				<view class="flex-wrap df ac">
					<view v-for="item in task.users" :key="item.id">
						<view class="flex-column">
							<avatar
								:size="24"
								:fontsize="26"
								:width="80"
								:name="
									item.name && item.name.length > 2
										? item.name.slice(1)
										: item.name
								"
							></avatar>
							<view class="process-name text-over">{{
								item.name
							}}</view>
						</view>
					</view>
				</view>
			</view>
		</view>
	</view>
</template>

<script>
import { ValueType } from './ComponentsConfigExport'
import { forEachNode, deepCopy } from './ProcessUtils'
import processApi from '@/subPackagesIndex/pages/index/approval/launch/apis/process.js'
import avatar from '@/subPackagesIndex/pages/index/approval/mainSub/components/avatar.vue'
export default {
	name: 'ProcessRender',
	components: { avatar },
	props: {
		forms: {
			type: Array,
			default: () => {
				return []
			},
		},
		pcMode: {
			type: Boolean,
			default: true,
		},
		process: {
			type: Object,
			default: () => {
				return {}
			},
		},
		formData: {
			type: Object,
			default: () => {
				return {}
			},
		},
		value: {
			type: Object,
			default: () => {
				return {}
			},
		},
		deptId: {
			type: String,
			default: null,
		},
	},
	computed: {
		loginUser() {
			return uni.getStorageSync('loginUser')
		},
		_value: {
			get() {
				return this.value
			},
			set(val) {
				this.$emit('input', val)
			},
		},
	},
	data() {
		return {
			loading: false,
			selectedNode: {},
			reverse: false,
			userCatch: {},
			oldFormData: {},
			processTasks: [],
			conditionFormItem: new Set(),
		}
	},
	mounted() {
		this.oldFormData = Object.assign({}, this.formData)
		this.loadProcessRender()
	},
	methods: {
		async loadProcessRender() {
			this.loading = true
			this.processTasks.length = 0
			this.loadProcess(this.process, this.processTasks)
			// this.processTasks.push({
			//   title: '结束',
			//   name: 'END',
			//   icon: 'el-icon-success',
			//   enableEdit: false
			// })
			this.loading = false
		},
		loadProcess(processNode, processTasks) {
			forEachNode(processNode, (node) => {
				switch (node.type) {
					// case 'ROOT':
					//   processTasks.push({
					//     id: node.id,
					//     title: node.name,
					//     name: '发起人',
					//     icon: 'el-icon-user-solid',
					//     enableEdit: false,
					//     users: [this.loginUser]
					//   });
					// break;
					case 'APPROVAL':
						processTasks.push(this.getApprovalNode(node))
						break
					case 'AUDIT':
						processTasks.push(this.getApprovalNode(node))
						break
					case 'CC':
						processTasks.push(this.getCcNode(node))
						break
					case 'CONDITIONS': //条件节点选一项
						this.getConditionNode(node, processTasks)
						this.loadProcess(node.children, processTasks)
						return true
					case 'CONCURRENTS':
						processTasks.push(this.getConcurrentNode(node))
						this.loadProcess(node.children, processTasks)
						return true
				}
			})
		},
		getApprovalNode(node) {
			let result = {
				id: node.id,
				title: node.name,
				name: node.type === 'APPROVAL' ? '审批人' : '办理人',
				icon: 'el-icon-s-check',
				enableEdit: false,
				multiple: false,
				mode: node.props.mode,
				users: [],
				desc: '',
			}
			let loadCatch = true
			switch (node.props.assignedType) {
				case 'ASSIGN_USER':
					result.users = deepCopy(node.props.assignedUser)
					result.desc = `指定${
						node.type === 'APPROVAL' ? '审批人' : '办理人'
					}`
					break
				case 'SELF':
					result.users = [this.loginUser]
					result.desc = `发起人自己${
						node.type === 'APPROVAL' ? '审批' : '办理'
					}`
					break
				case 'SELF_SELECT':
					result.enableEdit = true
					result.multiple = node.props.selfSelect.multiple || false
					result.desc = '自选审批人'
					break
				case 'LEADER_TOP':
					result.desc = '连续多级主管审批'
					processApi
						.getUserLeaders(
							'TOP' === node.props.leaderTop.endCondition
								? 0
								: node.props.leaderTop.endLevel,
							this.deptId,
							node.props.leaderTop.skipEmpty
						)
						.then((res) => {
							result.users = res.data
						})
					break
				case 'LEADER':
					result.desc =
						node.props.leader.level === 1
							? `直接主管${
									node.type === 'APPROVAL' ? '审批' : '办理'
							  }`
							: `第${node.props.leader.level}级主管${
									node.type === 'APPROVAL' ? '审批' : '办理'
							  }`
					processApi
						.getUserLeader(
							node.props.leader.level,
							this.deptId,
							node.props.leader.skipEmpty
						)
						.then((res) => {
							result.users = res.data ? [res.data] : []
						})
					break
				case 'ROLE':
					result.desc = `由角色[${(node.props.role || []).map(
						(r) => r.name
					)}]${node.type === 'APPROVAL' ? '审批' : '办理'}`
					processApi
						.getUsersByRoles(
							(node.props.role || []).map((r) => r.id)
						)
						.then((res) => {
							result.users = res.length > 2 ? [] : res
						})
					break
				case 'FORM_USER':
					loadCatch = false
					result.desc = `由表单字段内人员审批`
					this.conditionFormItem.add(node.props.formUser)
					result.users = this.formData[node.props.formUser] || []
					break
				case 'REFUSE':
					result.desc = `流程此处将被自动驳回`
					break
			}
			if (this.userCatch[node.id] && this.userCatch[node.id].length > 0) {
				result.users = this.userCatch[node.id]
			}
			if (loadCatch) {
				this.userCatch[node.id] = result.users
			}
			return result
		},
		getCcNode(node) {
			let result = {
				id: node.id,
				title: node.name,
				icon: 'el-icon-s-promotion',
				name: '抄送人',
				enableEdit: node.props.shouldAdd,
				type: 'org',
				multiple: true,
				desc: node.props.shouldAdd ? '可添加抄送人' : '',
				users: deepCopy(node.props.assignedUser),
			}
			if (this.userCatch[node.id] && this.userCatch[node.id].length > 0) {
				result.users = this.userCatch[node.id]
			}
			this.userCatch[node.id] = result.users
			return result
		},
		getConditionNode(node, processTasks) {
			//条件渲染，选一，取条件设置
			let trueNode = null
			for (let i = 0; i < node.branchs.length; i++) {
				const cdNode = node.branchs[i]
				let result = false,
					successNum = 0
				for (let j = 0; j < cdNode.props.groups.length; j++) {
					//计算单个组条件结果
					result = this.getConditionResultByGroup(
						cdNode.props.groups[j]
					)
					if (cdNode.props.groupsType === 'OR' && result) {
						//满足条件，那么这个节点就满足，执行该节点流程
						trueNode = cdNode
						break
					} else if (cdNode.props.groupsType === 'AND' && result) {
						successNum++
					}
				}
				if (trueNode) {
					break
				} else if (successNum === cdNode.props.groups.length) {
					//AND条件，全部都要满足
					trueNode = cdNode
					break
				}
			}
			if (trueNode) {
				//继续递归下面的流程
				this.loadProcess(trueNode, processTasks)
			} else {
				//条件均不满足，无法执行任何分支
				console.log(
					`条件节点 ${node.id} => ${node.name} 均不满足，无法继续`,
					node
				)
			}
		},
		getConditionResultByGroup(subCd) {
			let result = false,
				success = 0
			subCd.conditions.forEach((cd) => this.conditionFormItem.add(cd.id))
			for (let i = 0; i < subCd.conditions.length; i++) {
				let cd = subCd.conditions[i] //组内子条件
				switch (cd.valueType) {
					case ValueType.number:
						result = this.numberCompare(cd)
						break
					case ValueType.string:
						result = this.stringCompare(cd)
						break
					case ValueType.date:
						result = this.dateCompare(cd)
						break
					case ValueType.user:
						result = this.userCompare(cd)
						break
					case ValueType.array:
						result = this.arrayCompare(cd)
						break
				}
				if (result && subCd.groupType === 'OR') {
					break
				} else if (result && subCd.groupType === 'AND') {
					success++
				}
			}
			if (subCd.groupType === 'AND') {
				result = success === subCd.conditions.length
			}
			return result
		},
		getConcurrentNode(node) {
			let concurrentTasks = {
				id: node.id,
				title: node.name,
				name: '并行分支',
				icon: 'el-icon-s-operation',
				enableEdit: false,
				active: node.branchs[0].id, //激活得分支
				options: [], //分支选项，渲染单选框
				desc: '切换分支可显示对应执行流程',
				branchs: {}, //分支数据，不包含分支节点，key=分支子节点id，value = [后续节点]
			}
			node.branchs.forEach((nd) => {
				concurrentTasks.options.push({
					id: nd.id,
					title: nd.name,
				})
				this.$set(concurrentTasks.branchs, nd.id, [])
				this.loadProcess(nd.children, concurrentTasks.branchs[nd.id])
			})
			return concurrentTasks
		},
		getConcurrentSubNode(node) {
			return {
				id: node.id,
				title: node.name,
				name: '并行分支',
				icon: 'el-icon-s-operation',
				enableEdit: false,
			}
		},
		numberCompare(explain) {
			let val = this.formData[explain.id]
			switch (explain.compare) {
				case '>':
					return val > parseFloat(explain.value[0])
				case '<':
					return val < parseFloat(explain.value[0])
				case '=':
					return val === parseFloat(explain.value[0])
				case '>=':
					return val >= parseFloat(explain.value[0])
				case '<=':
					return val <= parseFloat(explain.value[0])
				case 'B':
					return (
						val > parseFloat(explain.value[0]) &&
						val < parseFloat(explain.value[1])
					)
				case 'AB':
					return (
						val >= parseFloat(explain.value[0]) &&
						val < parseFloat(explain.value[1])
					)
				case 'BA':
					return (
						val > parseFloat(explain.value[0]) &&
						val <= parseFloat(explain.value[1])
					)
				case 'ABA':
					return (
						val >= parseFloat(explain.value[0]) &&
						val <= parseFloat(explain.value[1])
					)
				case 'IN':
					return explain.value.indexOf(String(val)) > -1
				default:
					return false
			}
		},
		stringCompare(explain) {
			let val = this.formData[explain.id]
			switch (explain.compare) {
				case '=':
					return val === explain.value[0]
				case 'IN':
					return explain.value.indexOf(val) > -1
				default:
					return false
			}
		},
		dateCompare(explain) {},
		async userCompare(explain) {
			let userId =
				explain.id === 'root'
					? this.loginUser.id
					: this.formData[explain.id]
			//分离人和部门
			let users = [],
				depts = []
			explain.value.forEach((u) => {
				if (u.type === 'user') {
					users.push(u.id)
				} else {
					depts.push(u.id)
				}
			})
			let userCompare = users.indexOf(userId) > -1
			if (userCompare) {
				return userCompare
			} else {
				return 'true' === (await processApi.userInDepts(userId, depts))
			}
		},
		arrayCompare(explain) {
			let val = this.formData[explain.id]
			switch (explain.compare) {
				case '=':
					return val === explain.value[0]
				case 'IN':
					return explain.value.indexOf(val) > -1
				default:
					return false
			}
		},
		selected(users) {
			this._value[this.selectedNode.id] = []
			users.forEach((u) => {
				if (
					this.selectedNode.users.findIndex((v) => v.id === u.id) ===
					-1
				) {
					this.$set(u, 'enableEdit', true)
					this.selectedNode.users.push(u)
					this._value[this.selectedNode.id].push(u)
				}
			})
		},
		delUser(users, i) {
			users.splice(1, i)
		},
		addUser(node) {
			this.selectedNode = node
			this.$refs.orgPicker.show()
		},
		getApprovalMode(mode) {
			switch (mode) {
				case 'AND':
					return '会签'
				case 'NEXT':
					return '按顺序会签'
				case 'OR':
					return '或签'
				default:
					return '?'
			}
		},
		async formChangeHandler(newVal, oldVal) {
			this.conditionFormItem.forEach((key) => {
				if (newVal[key] !== oldVal[key]) {
					//重新加载流程
					console.log('执行流程重渲染')
					this.loadProcessRender()
				}
			})
			this.oldFormData = Object.assign({}, this.formData)
		},
	},
	watch: {
		formData: {
			deep: true,
			handler(val) {
				this.formChangeHandler(val, this.oldFormData)
			},
		},
	},
}
</script>
<style scoped lang="scss">
.main-color {
	color: #606266;
}
.flex-column {
	display: flex;
	flex-direction: column;
	align-items: center;
	margin-right: 10rpx;
	font-size: 27rpx;
}
.process-title {
	font-weight: 700;
	color: #101010;
}
.process-name {
	color: #9a9a9a;
	font-size: 28rpx;
	width: 85rpx;
	text-align: center;
	overflow-y: hidden;
}
.item-round {
	width: 10px;
	height: 10px;
	line-height: 20px;
	background-color: rgba(206, 206, 206, 1);
	border-radius: 50%;
}
.item-line {
	height: 120rpx;
	width: 3rpx;
	background-color: rgba(206, 206, 206, 1);
}
.title-desc {
	width: max-content;
}
</style>
